//
//  Leaks.swift
//  Documentation
//
//  Created by Toj on 7/31/23.
//

import Foundation


// MARK: - Leaks 介绍
/** 从苹果官方文档可知，一个app的内存主要分3类
 * 1. Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument).
 ** 内存泄露: 应用程序未引用的内存，不能再次使用或释放
 * 2. Abandoned memory: Memory still referenced by your application that has no useful purpose.
 ** 废弃内存: 应用程序仍然引用的内存, 没有任何目标使用它.
 * 3. Cached memory: Memory still referenced by your application that might be used again for better performance.
 ** 缓存内存: 应用程序仍然引用的内存, 可能会再次使用以获得更好的性能.
 
 ** NOTE:
 其中 Leaked memory 和 Abandoned memory 都属于应该释放而没释放的内存, 都是内存泄露,
 而 Leaks 工具只负责检测 Leaked memory, 而不管 Abandoned memory.
 
 1. 对于 Abandoned memory, 可以用 Instrument 的 Allocations 检测出来.
 检测方法是用 Mark Generation 的方式,
 当你每次点击 Mark Generation 时, Allocations 会生成当前 App 的内存快照，
 而且 Allocations 会记录从上回内存快照到这次内存快照这个时间段内, 新分配的内存信息.
 缺点是需要重复操作，其无法及时得知泄漏.
 2. 对于 Leaked memory, 可以使用Leaks 工具检测, 适用于运行时的检测.
 */

// MARK: - Detail Area
/** 主要包含 4个统计项
 * 1. Statistics: 统计数据表
 * 2. Call Trees: 调用树
 * 3. Allocations List: 配置列表
 * 4. Generations: 快照
 */


// MARK: 1. Statistics: 统计数据表
/** 主要包含12个统计字段, 其中6个为主要, 分别对应的含义如下
 * 1. Category: 对象类型, 包含Core Foundation对象、OC类、内存块
 * 2. Persistent: 当前活跃的内存存储大小
 * 3. #Persistent: 当前活跃的内存数量
 * 4. #Transient: 已经释放的内存申请数
 * 5. Total Bytes: 总字节数
 * 6. #Total: 申请内存总次数
 */

// MARK: 2. Call Trees: 调用树
/**
* 1. Bytes Used: 相应方法使用的内存量
* 2. Count: 方法被调用的次数
* 3. Symbol Name: 方法名称
*/

// MARK: 2.1 Call Trees: Filter
/** 在Call Trees下方还有三个配置项
 * 1. Call Tree: 配置调用树的显示, 一般勾选【1、3、4、5】
 ** 1.1、Separate by Category: 按类别隔开, 可以更方便的看出是哪些类别的VM
 ** 1.2、Separate by Thread: 按线程划分
 ** 1.3、Invert Call Tree: 反转调用, 通俗来说就是从上倒下跟踪堆栈, 把调用层级最深的显示在最外面
 ** 1.4、Hide System Libraries: 隐藏系统方法, 因为目前我们只关心自定义的方法, 这个是必选的
 ** 1.5、Flatten Recursion: 递归函数，每个堆栈跟踪一个条目
 * 2. Call Tree Constraints: 主要是对列表中的数据进行过滤, 可以设置最大/最小的数量和字节数
 * 3. Data Mining: 数据挖掘.
 简单来说就是可以过滤掉不看的库、符号调用, 点击Symbol、Library会自动把你选中的行的符号、库加到小框中.
 例如选中start, 点击symbol, 就自动过滤了start相关列
 */

// MARK: 3. Allocations List: 配置列表
/**
 * 1: Adress: 内存块的地址
 * 2: Category: 对象的类型
 * 3: Timestamp: 申请内存的时间
 * 4: Size: 内存块的大小
 * 5: Responsible Library: 负责申请该内存的库
 * 6: Responsible Caller: 负责申请该内存的方法
 */

// MARK: 4. Generations: 快照
/**
 * NOTE: 在Allocation中, 可以通过对每个动作的前后进行Mark Generation, 用来对比内存的增加, 可以定位到内存增加的具体方法和代码所在位置.
 * 其中有4列统计项
* 1. Snapshot: 快照名
* 2. Timestamp: 快照时间
* 3. Heap Growth: 自上次快照以来的增长数量
* 4. #Persistent: 依旧存活的对象数量
*/

// MARK: - Filter
/** 下方包含三个选项
 * 1. Mark Generation: 生成当前 App 的内存快照
 * 2. Allocation Lifespan: 筛选需要记录的Allocation, 一般我们只勾选Created & Persistent
 ** 2.1、All Allocations: 所有的
 ** 2.2、Created & Persistent: 创建且存活的
 ** 2.3、Created & Destroyed: 创建且被销毁的
 
 * 3. Allocation Type: 记录Allocation的类型, 一般关注All Heap Allocations较多
 ** 3.1、All Heap & Anonymous VM: 所有真实内存和虚拟内存
 ** 3.2、All Heap Allocations: 所有真实内存
 ** 3.3、All VM Regions: 所有分配过的虚拟内存
 * 4. Snapshots 设计间隔几秒检测一次
 */

// MARK: - 实战
/** 主要分两部分
 * 1. 检测内存泄漏
 * Instruments - Leaks: 然后运行程序，随着对模拟器运行的App的操作, 可以在Leaks中查看内存占用的情况
 * 其中对号✅ 表示 不存在内存泄漏, ×❌表示存在内存泄漏,
 * 将竖线移动到内存泄漏点, 选择底部的Call Tree
 * 在弹窗中选择Invert Call Tree 和 Hide System Librarie,
 * 即可显示出具体内存泄漏的代码, 点击最左侧的箭头
 *
 * 2. 查看具体的内存泄漏情况
 * 将Call Tree选项改为Cycles & Roots 即可查看
 */
